package org.edu.jgame.cardgames;


import org.newdawn.slick.*;

import java.awt.Font;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Random;
import java.util.Scanner;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.state.*;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class GameplayState extends BasicGameState{

	int stateID = -1;	
	
	static final int SHUFFLE = 1;			// Shuffle deck
	static final int DEALING = 2;			// thay doi Dealing -> Playing
	static final int READYPASS = 3;
	static final int PASS = 4;	
	static final int PLAYING = 5;	
	static final int END = 6;
	static final int SHOOTTHEMOON = 7;
	static final int ENDTRICK = 8;
	
	static int cur_state = 0;	
	
	static volatile CardStore store;
	
	Image[] deckresource = null;
	
	Image background = null;
	Image[] quit = null;
	Image[]	deck = null;
	Image[] arrow = null;
	
	int idx_quit = 0;
	int idx_deck = 0;
	int idx_arrow = 0;
	
	float quitx = 0.0f;
	float quity = 0.0f;
	float deckx = 0.0f;
	float decky = 0.0f;
	float arrowx = 0.0f;
	float arrowy = 0.0f;
	
	static Player[] player = null;
	static int[] player_point = null;
	static int pos = -1;
	int count_heart = 0;
	
	int pass_direct = 0;

	int timer = 1500;
	int count = 0;
	
	int cur_player = -1;
	boolean highlight = false;
	
	int direction;
	UnicodeFont unicodeFont = null;
	
	int[] order_draw = null;
	
	static int cur_suit = 0;
	static boolean is_heart_out = false;
	static boolean finish_shuffle = true;
	static boolean finish_pass = true;
	static boolean finish_lead = true;
	
	int winner = 0;
	Sound sclick = null;
	Sound shuffle_sound = null;
	boolean sclick_play = false;
	boolean inside_quit = false;
	
	// For network data

	public volatile String[] player_name = new String[4];
	public volatile int[] player_id = new int[4];
	public volatile int[][] cards = new int[4][13];	
	
	public GameplayState() {
		
	}
	
	
	public GameplayState(int stateID) {
		this.stateID = stateID;	
		
		player_point = new int[4];
		for(int i = 0; i < 4; i++) {
			player_point[i] = 0;
		}
	}	
	
	private boolean compareCard(int a, int b) {
		int a1 = a/10;
		int a2 = a%10;
		
		int b1 = b/10;
		int b2 = b%10;
		
		if(a2 > b2) {	// Compare card type 
			return true;
		}
		
		if((a2 == b2) && (a1 > b1)) {	// Compare card number
			return true;
		}
		
		return false;
	}
	
	
	private void BubbleSort(int[] arr) {
		boolean swapped = true;
		int j = 0;
		int tmp;
		while (swapped) {
			swapped = false;
			j++;
			for (int i = 0; i < arr.length - j; i++) {
				if (compareCard(arr[i], arr[i+1])) {
					tmp = arr[i];
					arr[i] = arr[i + 1];
					arr[i + 1] = tmp;
					swapped = true;
				}
			}
		}
	}	
	
	
	private void getData(String ResponseLine) throws Exception {
		DocumentBuilder db = DocumentBuilderFactory.newInstance()
				.newDocumentBuilder();

		org.w3c.dom.Document doc = db.parse(new ByteArrayInputStream(
				ResponseLine.getBytes("UTF-8")));

		NodeList nodes = doc.getElementsByTagName("p");

		for (int i = 0; i < nodes.getLength(); i++) {
			Element element = (Element) nodes.item(i);
			//System.out.println("Name: " + element.getAttribute("name"));
			player_name[i] = element.getAttribute("name");

			//System.out.println("Id: " + element.getAttribute("id"));
			player_id[i] = Integer.parseInt(element.getAttribute("id"));
			for (int j = 0; j < element.getElementsByTagName("card")
					.getLength(); j++) {
				Element card = (Element) element.getElementsByTagName("card")
						.item(j);
				//System.out.println("Card:" + card.getAttribute("value"));
				cards[i][j] = Integer.parseInt(card.getAttribute("value"));
			}
		}
	}
	
	private void shuffle1() throws SlickException {			
		
		// Sort the card value before create them		
			
		player = new Player[4];
		for(int i = 0; i < 4; i++) {
			player[i] = new Player(player_name[i], player_id[i], i);
			player[i].setScore(player_point[i]);
			player[i].initCard(cards[i]);
			
			for(int j = 0; j < cards[i].length; j++) {
				cards[i][j] = 0;
			}
		}
		
		finish_shuffle = true;	
	}
	
	@Override
	public void init(GameContainer gameContainer, StateBasedGame sbg)
			throws SlickException {		
		
		// Background - no need to say
		background = new Image("data/background.png");
		
		// Quit button Sprite
		quit = new Image[2];
		quit[0] = new Image("data/QuitMatch_Button.png");
		quit[1]	= new Image("data/QuitMatch_Button_Select.png");
		quitx = 29;
		quity = 25;
		idx_quit = 0;
		
		// Deck Sprite
		deckresource = new Image[4];
		deckresource[0] = new Image("data/Deck1-1.png");
		deckresource[1] = new Image("data/Deck1-2.png");
		deckresource[2] = new Image("data/Deck2-1.png");
		deckresource[3] = new Image("data/Deck2-2.png");
		
		deck = new Image[2];
		deckx = 400 - deckresource[0].getWidth()/2;
		decky = 300 - deckresource[0].getHeight()/2;
		idx_deck = 0;
		
		// Arrow Sprite
		arrow = new Image[3];
		arrow[0] = new Image("data/Left.png");
		arrow[1] = new Image("data/Right.png");
		arrow[2] = new Image("data/Accross.png");		
		arrowx = 400;
		arrowy = 400;
		
		unicodeFont = new UnicodeFont(new Font("Arial", Font.BOLD, 40));
		unicodeFont.addAsciiGlyphs();
		unicodeFont.getEffects().add(new ColorEffect(java.awt.Color.WHITE));
		unicodeFont.loadGlyphs();	
		
		sclick = new Sound("data/click.wav");
		shuffle_sound = new Sound("data/shuffle.wav");
		
		order_draw = new int[4];
		
		store = new CardStore();
		
		cur_state = SHUFFLE;			
	}
	
	public void endTrick(GameContainer gameContainer, int delta) throws SlickException {
		int value = 0;
		int type = 0;
		
		// Get the suit of this trick by getting the value and type of the first card
		switch(cur_player) {
		case 0:
			value = player[0].getLeadCardValue();
			type = player[0].getLeadCardType();
			break;
		case 1:
			value = player[1].getLeadCardValue();
			type = player[1].getLeadCardType();
			break;
		case 2:
			value = player[2].getLeadCardValue();
			type = player[2].getLeadCardType();
			break;
		case 3:
			value = player[3].getLeadCardValue();
			type = player[3].getLeadCardType();
			break;
		}	
		

		
		for(int i = 0; i < 4; i++) {
			if(player[i].getLeadCardType() == type && player[i].getLeadCardValue() >= value) {
				value = player[i].getLeadCardValue();
				pos = i;
			}
		}		
		
		count_heart = 0;
		
		// count the number of heart
		for(int i = 0; i < 4; i++) {
			if(player[i].getLeadCardType() == 4) {
				count_heart ++;
			}
			else if(player[i].getLeadCardValue() == 12 && player[i].getLeadCardType() == 3) {
				count_heart += 13;
			}
		}
	}
	
	public void moveToWinner() {
		for(int i = 0; i < 4; i++) {
			player[i].moveToWinner(pos);
		}
	}
		
	static public void changeSuit(int t) {
		if(cur_suit == 0) {
			cur_suit = t;
		}
	}
	
	static public int getSuit() {
		return cur_suit;
	}
	
	private void findFirstOne() {
		if(player[0].theFirstOne()) {
			cur_player = 0;
			player[0].setPlay();
			
			order_draw[0] = 0;
			order_draw[1] = 1;
			order_draw[2] = 2;
			order_draw[3] = 3;
		}
		if(player[1].theFirstOne()) {
			cur_player = 1;
			player[1].setPlay();
			
			order_draw[0] = 1;
			order_draw[1] = 2;
			order_draw[2] = 3;
			order_draw[3] = 0;
		}
		if(player[2].theFirstOne()) {
			cur_player = 2;
			player[2].setPlay();
			
			order_draw[0] = 2;
			order_draw[1] = 3;
			order_draw[2] = 0;
			order_draw[3] = 1;
		}
		if(player[3].theFirstOne()) {
			cur_player = 3;
			player[3].setPlay();
			
			order_draw[0] = 3;
			order_draw[1] = 0;
			order_draw[2] = 1;
			order_draw[3] = 2;
		}
	}

	private void shootTheMoon() {
		for(int i = 0; i < 4; i++) {
			if(player[i].getCurScore() == 26) {
				player[i].shootTheMoon(true);
				for(int j = 0; j < 4; j++) {
					if(j != i) {
						player[j].shootTheMoon(false);
					}
				}
			}			
		}
	}
	
	static public void heartOut() {
		is_heart_out = true;
	}
	
	@Override
	public void render(GameContainer gameContainer, StateBasedGame sbg, Graphics graphics)
			throws SlickException {		
		background.draw();
		quit[idx_quit].draw(quitx, quity);
		
		// Draw the deck. The deck only exist in shuffle and dealing state		
		
		if(cur_state == DEALING || cur_state == PLAYING || cur_state == PASS || cur_state == ENDTRICK) {
			if(finish_shuffle) {
				for (int i = 0; i < 4; i++) {
					player[order_draw[i]].render(gameContainer, graphics,
							Player.DEALING);
				}
			}
		}
		
		if(cur_state == READYPASS) {
			for(int i = 0; i < 4; i++) {
				player[order_draw[i]].render(gameContainer, graphics, Player.DEALING);
			}
			if(highlight == true) {
				arrow[idx_arrow].draw(arrowx - arrow[idx_arrow].getWidth()/2, arrowy - arrow[idx_arrow].getHeight()/2, Color.orange);
			}
			else {
				arrow[idx_arrow].draw(arrowx - arrow[idx_arrow].getWidth()/2, arrowy - arrow[idx_arrow].getHeight()/2);
			}
		}
		
		if(cur_state == SHUFFLE || cur_state == DEALING) {
			deck[idx_deck].draw(400 - deck[0].getWidth()/2, 300 - deck[0].getHeight()/2);
		}	
		
		if(cur_state == END) {
			background.draw(0, 0, Color.gray);
			unicodeFont.drawString(220, 200, player[winner].name + " WIN");
		}
		
	}
	
	@Override
	public void update(GameContainer gameContainer, StateBasedGame sbg, int delta)
			throws SlickException {		
		
		Input input = gameContainer.getInput();			
		
		try {
			if(is.available() > 0) {
				ResponseLine = is.readLine();
			}
		}
		catch(Exception e) {}
				
		
		if(cur_state == SHUFFLE) {
			timer -= delta;
			count ++;
			if(!shuffle_sound.playing()) {
				shuffle_sound.play();
			}
			
			if(count == 1) {
				try {
					FileInputStream fis = new FileInputStream("data/config");
					Scanner scanner = new Scanner(fis);
					scanner.nextLine();
					String s = "";
					
					if(scanner.hasNext()) {
						s = scanner.nextLine();
					}
					s = s.split(" ")[1];
					if(s.equalsIgnoreCase("data/b1fv.png")) {
						deck[0] = deckresource[0].copy();
						deck[1] = deckresource[1].copy();
					}
					else {
						deck[0] = deckresource[2].copy();
						deck[1] = deckresource[3].copy();
					}
				}
				catch(FileNotFoundException e) {}
				catch(IOException e) {}
				
				timer = 500;
			}
			
			if(count == 50) {
				count = 0;
				idx_deck = (idx_deck == 0) ? 1 : 0;				
			}
			
			if(timer <= 0) {
				timer = 3;		
				count = 0;
				idx_deck = 0;				
				
				changeState(DEALING);				
				os.println("endshuffle");
					
				finish_shuffle = false;
				order_draw[0] = 0;
				order_draw[1] = 1;
				order_draw[2] = 2;
				order_draw[3] = 3;	
				
				shuffle_sound.stop();
			}			
		}
		
		if(cur_state == DEALING) {
			
			if(!finish_shuffle) {
				if(ResponseLine.startsWith("<data>")) {
					try {
						getData(ResponseLine);
						
						shuffle1();
						ResponseLine = "";
					}
					catch(Exception e) {
						
					}
				}
				
			}
			else {
				timer -= delta;


				if(timer <= 0) {
					for(int i = 0; i < 4; i++) {
						player[i].update(gameContainer, delta, Player.DEALING);
					}

					timer = 3;
					if(player[0].getCurState() == Player.READY &&  player[1].getCurState() == Player.READY
							&& player[2].getCurState() == Player.READY && player[3].getCurState() == Player.READY) {
						changeState(READYPASS);
						timer = 10;
						
						for(int i = 0; i < 4; i++) {
							player[i].setDirection(pass_direct);
						}

						// Xoa cho nay
						//player[1].autopass();
						//player[2].autopass();
						//player[3].autopass();
						
						count = 0;
					}
				}
			}
		}		
		
		if(cur_state == READYPASS) {
			if(pass_direct == 3) {
				pass_direct = 0;
				idx_arrow = 0;
				changeState(PLAYING);
			}
			for(int i = 0; i < 4; i++) {
				player[i].update(gameContainer, delta, Player.READY);
			}

			if ((input.getMouseX() >= arrowx - arrow[idx_arrow].getWidth() / 2 && input
					.getMouseX() <= arrowx + arrow[idx_arrow].getWidth() / 2)
					&& (input.getMouseY() >= arrowy
							- arrow[idx_arrow].getHeight() / 2 && input
							.getMouseY() <= arrowy
							+ arrow[idx_arrow].getHeight() / 2)) {
				highlight = true;
				if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON) && player[0].readyPass()) {
					changeState(PASS);		
					// gui tin hieu
					
					String pass = "<p name='" + player[0].name + "' id='" + player[0].playerID + "'>";
					pass += player[0].getpass() + "</p>";
					os.println(pass);
					os.println("pass");
					finish_pass = false;
				}
			}
			else {
				highlight = false;
			}
		}
		
		if(cur_state == PASS) {			
			if(!finish_pass) {
				if(ResponseLine.startsWith("<data>")) {
					try {
						getData(ResponseLine);

						for (int i = 1; i < 4; i++) {
							for (int j = 0; j < 4; j++) {
								if (player[i].getPlayerID() == player_id[j]) {
									player[i].setpass(cards[j]);
									break;
								}
							}
						}

						finish_pass = true;
					} catch (Exception e) {

					}
				}
			}
			else {
				for(int i = 0; i < 4; i++) {
					player[i].update(gameContainer, delta, Player.PASS);
				}		
			}
			
			
			if(player[0].getCurState() == Player.PLAYING && player[1].getCurState() == Player.PLAYING
					&& player[2].getCurState() == Player.PLAYING && player[3].getCurState() == Player.PLAYING) {
				int[] a0 = player[0].getPass();
				int[] a1 = player[1].getPass();
				int[] a2 = player[2].getPass();
				int[] a3 = player[3].getPass();

				switch (pass_direct) {
				case 0: // Pass left
					player[0].takePass(a3);
					player[1].takePass(a0);
					player[2].takePass(a1);
					player[3].takePass(a2);
					break;
				case 1:
					player[0].takePass(a1);
					player[1].takePass(a2);
					player[2].takePass(a3);
					player[3].takePass(a0);
					break;
				case 2:
					player[0].takePass(a2);
					player[1].takePass(a3);
					player[2].takePass(a0);
					player[3].takePass(a1);
					break;
				}
				idx_arrow++;
				if (idx_arrow >= 3) {
					idx_arrow = 0;
				}

				pass_direct++;
				if (pass_direct > 3) {
					pass_direct = 0;
				}

				changeState(PLAYING);
				os.println("play");
				
				findFirstOne();
				finish_lead = false;
				timer = 10;
			}							
		}
		
		if(cur_state == ENDTRICK) {
			
			for(int i = 0; i < 4; i++) {
				player[i].moveToWinner(pos);
			}
			
			if(player[0].getCurState() == Player.PLAYING &&  player[1].getCurState() == Player.PLAYING
					&& player[2].getCurState() == Player.PLAYING && player[3].getCurState() == Player.PLAYING) {
				changeState(PLAYING);				

				for(int i = 0; i < 4; i++) {
					player[i].setWinner(pos, count_heart);
					player[i].stopPlay();
				}				
				count = 0;
				
				cur_suit = 0;
				cur_player = pos;
				finish_lead = false;
				
				for(int i = 0; i < 4; i++) {
					if(i == cur_player) {
						player[i].setPlay();
					}
					order_draw[i] ++;
					if(order_draw[i] > 3) {
						order_draw[i] = 0;
					}
				}
				
				for(int i = 0; i < 4; i++) {
					player[i].update(gameContainer, delta, Player.ENDTRICK);
				}
			}
		}
		
		
		if(cur_state == PLAYING) {
			timer -= delta;
			
			if(timer <= 0) {
				
				if(count == 4) {	// Finish one trick and calculate the score
					endTrick(gameContainer, delta);
					changeState(ENDTRICK);
				}
				else {
					
					for(int i = 0; i < 4; i++) {
						if(i == cur_player) {
							if(player[i].isLead()) {
								player[i].setPlay();
								// truyen len socket chuoi card
								if(i == 0 && player[i].isleadready() && !finish_lead) {
									String pass = "<p name='" + player[0].name + "' id='" + player[0].playerID + "'>";
									pass += player[0].getleadcard() + "</p>";
									os.println(pass);
									finish_lead = true;
								}
								else {
									if(ResponseLine.startsWith("<data>")) {
										try{
											getData(ResponseLine);
											player[i].setleadcard(cards[0][0]);
											ResponseLine = "";
										}
										catch(Exception e) {}
									}
								}
							}
							else {
								int t = i+1;
								t = t > 3 ? 0 : t;
								player[t].setPlay();
								cur_player = t;
								count ++;
							}
							break;
						}
					}
				}
				
				for(int i = 0; i < 4; i++) {
					player[i].update(gameContainer, delta, Player.PLAYING);
				}			
				
				timer = 10;
				
				if(player[0].getCurState() == Player.END &&  player[1].getCurState() == Player.END
						&& player[2].getCurState() == Player.END && player[3].getCurState() == Player.END) {
					endTrick(gameContainer, delta);
					
					shootTheMoon();
					
					for(int i = 0; i < 4; i++) {
						player[i].calTotal();
						player_point[i] = player[i].getTotalScore();
						if(player_point[i] >= 10) {
							changeState(END);
							timer = 500;
						}
					}				
					
					if(cur_state != END) {						
						changeState(SHUFFLE);
						is_heart_out = false;
						cur_suit = 0;
						timer = 500;
					}
					else {
						int idx_min = 0;
						for(int i = 0; i < 4; i++) {
							if(player[i].getTotalScore() < player[idx_min].getTotalScore()) {
								idx_min = i;
							}
						}
						winner = idx_min;
					}
					
				}
			}
		}
		
		if(cur_state == END) {
			timer -= delta;
			if(timer <= 0) {
				changeState(SHUFFLE);
				timer = 500;
				
				for(int i = 0; i < 4; i++) {
					player_point[i] = 0;
				}
				
				is_heart_out = false;
				cur_suit = 0;
				idx_arrow = 0;				
				pass_direct = 0;
				closed = true;
				os.println("quit");
				os.close();				
				
				try {
					is.close();
					clientSocket.close();
				}
				catch(Exception e) {}
				
				sbg.enterState(JCardGame.MAINMENUSTATE);			
			}
		}
		
		if(inside_quit) {
			if(!sclick_play) {
				sclick.play();
				sclick_play = true;
			}
		}
		
		if((input.getMouseX() >= quitx && input.getMouseX() <= quitx + quit[idx_quit].getWidth())
				&& (input.getMouseY() >= quity && input.getMouseY() <= quity + quit[idx_quit].getHeight())) {
			idx_quit = 1;
			inside_quit = true;
			
			if(input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) {
				changeState(SHUFFLE);
				timer = 500;
				
				for(int i = 0; i < 4; i++) {
					player_point[i] = 0;
				}
				
				is_heart_out = false;
				cur_suit = 0;
				idx_arrow = 0;				
				pass_direct = 0;
				closed = true;
				os.println("quit");
				os.close();
				
				
				try {
					is.close();
					clientSocket.close();
				}
				catch(Exception e) {}
				
				sbg.enterState(JCardGame.MAINMENUSTATE);
			}
		}
		else {
			idx_quit = 0;
			inside_quit = false;
			sclick_play = false;
		}		
	}
	
	static public void changeState(int state) {
		cur_state = state;		
	}

	@Override
	public int getID() {		
		return stateID;
	}
	
	static public void SetNetwork(Socket socket, PrintStream oss, DataInputStream iss, boolean closeds) {
		clientSocket = socket;
		os = oss;
		is = iss;
		closed = closeds;
	}
	
	// Network part
	static Socket clientSocket = null;
	static PrintStream os = null;
	static DataInputStream is = null;
	static BufferedReader inputLine = null;
	static boolean closed = true;
	static String ResponseLine = "";
	
	public void connect() {
		int port_number = 2222;
		String host = "localhost";

		System.out.println("Usage: java MultiThreadChatClient  \n"
				+ "Now using host=" + host + ", port_number=" + port_number);
		
		// Initialization section:
		// Try to open a socket on a given host and port
		// Try to open input and output streams
		try {
			clientSocket = new Socket(host, port_number);
			closed = false;
			
			inputLine = new BufferedReader(new InputStreamReader(System.in));
			os = new PrintStream(clientSocket.getOutputStream());
			is = new DataInputStream(clientSocket.getInputStream());
		} catch (UnknownHostException e) {
			System.err.println("Don't know about host " + host);
		} catch (IOException e) {
			System.err
					.println("Couldn't get I/O for the connection to the host "
							+ host);
		}
			
	}
	
}
